/**
 * 拖拽上传指令
 * @description 为Vue元素添加拖拽上传功能，支持文件类型过滤、多文件上传、禁用状态等
 * @author 系统开发团队
 * @version 1.0.0
 */

import type { Directive, DirectiveBinding } from 'vue'

// ==================== 类型定义 ====================

/**
 * 扩展HTMLElement类型，添加拖拽上传相关属性
 * @description 为HTMLElement添加拖拽事件处理器存储属性
 */
interface HTMLElementWithDragUpload extends HTMLElement {
    /** 拖拽上传事件处理器集合 */
    _dragUploadHandlers?: {
        /** 拖拽进入事件处理器 */
        handleDragEnter: (e: DragEvent) => void
        /** 拖拽离开事件处理器 */
        handleDragLeave: (e: DragEvent) => void
        /** 拖拽悬停事件处理器 */
        handleDragOver: (e: DragEvent) => void
        /** 拖拽放置事件处理器 */
        handleDrop: (e: DragEvent) => void
    }
}

/**
 * 拖拽上传配置选项接口
 * @description 定义拖拽上传指令的各种配置选项
 */
interface DragUploadOptions {
    /** 文件放置时的回调函数 */
    onDrop?: (files: FileList) => void
    /** 拖拽进入时的回调函数 */
    onDragEnter?: () => void
    /** 拖拽离开时的回调函数 */
    onDragLeave?: () => void
    /** 拖拽悬停时的回调函数 */
    onDragOver?: () => void
    /** 接受的文件类型，支持MIME类型和文件扩展名 */
    accept?: string
    /** 是否允许多文件上传 */
    multiple?: boolean
    /** 是否禁用拖拽上传功能 */
    disabled?: boolean
}

// ==================== 拖拽上传指令 ====================

/**
 * 拖拽上传Vue指令
 * @description 提供完整的拖拽上传功能，包括文件类型过滤、多文件支持、状态管理等
 * @example
 * // 在模板中使用
 * <div v-drag-upload="{
 *   onDrop: handleFiles,
 *   accept: 'image/*,.pdf',
 *   multiple: true
 * }">
 *   拖拽文件到此处上传
 * </div>
 */
const dragUpload: Directive = {
    /**
     * 指令挂载时调用
     * @description 为元素添加拖拽上传功能，绑定相关事件监听器
     * @param {HTMLElementWithDragUpload} el 绑定的DOM元素
     * @param {DirectiveBinding} binding 指令绑定对象
     */
    mounted(el: HTMLElementWithDragUpload, binding: DirectiveBinding) {
        const options: DragUploadOptions = binding.value || {}

        // 添加拖拽相关样式类
        el.classList.add('drag-upload-target')

        /**
         * 处理拖拽进入事件
         * @description 当文件拖拽进入目标区域时触发
         * @param {DragEvent} e 拖拽事件对象
         */
        const handleDragEnter = (e: DragEvent) => {
            e.preventDefault()
            e.stopPropagation()

            if (options.disabled) return

            // 添加拖拽悬停样式
            el.classList.add('drag-over')
            options.onDragEnter?.()
        }

        /**
         * 处理拖拽离开事件
         * @description 当文件拖拽离开目标区域时触发，包含边界检测
         * @param {DragEvent} e 拖拽事件对象
         */
        const handleDragLeave = (e: DragEvent) => {
            e.preventDefault()
            e.stopPropagation()

            if (options.disabled) return

            // 检查是否真的离开了元素边界
            const rect = el.getBoundingClientRect()
            const x = e.clientX
            const y = e.clientY

            if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
                el.classList.remove('drag-over')
                options.onDragLeave?.()
            }
        }

        /**
         * 处理拖拽悬停事件
         * @description 当文件在目标区域上方悬停时触发
         * @param {DragEvent} e 拖拽事件对象
         */
        const handleDragOver = (e: DragEvent) => {
            e.preventDefault()
            e.stopPropagation()

            if (options.disabled) return

            // 设置拖拽效果为复制
            if (e.dataTransfer) {
                e.dataTransfer.dropEffect = 'copy'
            }

            options.onDragOver?.()
        }

        /**
         * 处理文件放置事件
         * @description 当文件放置到目标区域时触发，包含文件类型过滤和处理
         * @param {DragEvent} e 拖拽事件对象
         */
        const handleDrop = (e: DragEvent) => {
            e.preventDefault()
            e.stopPropagation()

            if (options.disabled) return

            // 移除拖拽悬停样式
            el.classList.remove('drag-over')

            const files = e.dataTransfer?.files
            if (files && files.length > 0) {
                // 过滤文件类型
                let filteredFiles = Array.from(files)

                if (options.accept) {
                    const acceptTypes = options.accept.split(',').map(type => type.trim())
                    filteredFiles = filteredFiles.filter(file => {
                        return acceptTypes.some(type => {
                            if (type.startsWith('.')) {
                                // 文件扩展名匹配（如 .jpg, .pdf）
                                return file.name.toLowerCase().endsWith(type.toLowerCase())
                            } else if (type.includes('*')) {
                                // MIME类型通配符匹配（如 image/*, text/*）
                                const pattern = type.replace('*', '.*')
                                return new RegExp(pattern).test(file.type)
                            } else {
                                // 精确MIME类型匹配（如 image/jpeg）
                                return file.type === type
                            }
                        })
                    })
                }

                // 如果有符合条件的文件，创建FileList并调用回调
                if (filteredFiles.length > 0) {
                    const fileList = new DataTransfer()
                    const filesToAdd = options.multiple ? filteredFiles : [filteredFiles[0]]

                    filesToAdd.forEach(file => {
                        fileList.items.add(file)
                    })

                    options.onDrop?.(fileList.files)
                }
            }
        }

        // 绑定事件监听器到元素
        el.addEventListener('dragenter', handleDragEnter)
        el.addEventListener('dragleave', handleDragLeave)
        el.addEventListener('dragover', handleDragOver)
        el.addEventListener('drop', handleDrop)

        // 存储事件监听器引用，用于后续清理
        el._dragUploadHandlers = {
            handleDragEnter,
            handleDragLeave,
            handleDragOver,
            handleDrop
        }
    },

    /**
     * 指令更新时调用
     * @description 当指令的值发生变化时更新元素状态
     * @param {HTMLElementWithDragUpload} el 绑定的DOM元素
     * @param {DirectiveBinding} binding 指令绑定对象
     */
    updated(el: HTMLElementWithDragUpload, binding: DirectiveBinding) {
        const options: DragUploadOptions = binding.value || {}

        // 根据禁用状态更新样式类
        if (options.disabled) {
            el.classList.add('drag-disabled')
        } else {
            el.classList.remove('drag-disabled')
        }
    },

    /**
     * 指令卸载时调用
     * @description 清理所有事件监听器和相关属性
     * @param {HTMLElementWithDragUpload} el 绑定的DOM元素
     */
    unmounted(el: HTMLElementWithDragUpload) {
        // 清理事件监听器
        if (el._dragUploadHandlers) {
            const handlers = el._dragUploadHandlers
            el.removeEventListener('dragenter', handlers.handleDragEnter)
            el.removeEventListener('dragleave', handlers.handleDragLeave)
            el.removeEventListener('dragover', handlers.handleDragOver)
            el.removeEventListener('drop', handlers.handleDrop)
            delete el._dragUploadHandlers
        }
    }
}

export default dragUpload 